<?php


namespace custom;


use app\admin\model\Admin;
use app\wechat\model\User;
use app\badmin\model\CompanyAccount;
use app\common\lib\RedisKey;
use app\common\lib\RespCode;
use think\Exception;
use app\api\model\CompanyAgent;
use app\apienc\model\CompanyAgent as CompanyAgentenc;

class Token {

    /**
     * A端erp token
     */
    const ADMIN_TOKEN = 'admin_token';

    /**
     * b端erp token
     */
    const BADMIN_TOKEN='badmin_token';

    /**
     * api token
     */
    const API_TOKEN = 'api_token';

    /**
     * wechat token
     */
    const WECHAT_TOKEN = 'wechat_token';

    /**
     * 测试加密/解密 token
     */
    const APIENC_TOKEN='apienc_token';
    /**
     * 过期时间，一个月
     */
    const EXPIRE_TIME = 2592000;

    /**
     * 生成token
     * @param $uid
     * @param string $type
     * @param int $expire
     * @return string
     * @throws Exception
     */
    public function getToken($uid,$headers, $type, $expire,$keyer) {
        $token = strtoupper(md5(get_nonce_str() . $uid . time())) . base64_encode(get_nonce_str() . $uid);
        $redisKey = $keyer . "_{$type}";//表
        $redisModel = Redis::getInstance();
        $tokenData = [
            'expire'    => time() + $expire,
            // 是微信唯一id
            'is_openid' => !defined('IS_OPENID') ? false : true,
            'token'     => $token,
        ];
        if(isset($headers['devsn'])){
            $tokenData['devsn']=$headers['devsn'];
        }
        // 生成token映射
        if ($redisModel->hset($redisKey, $uid, json_encode($tokenData)) !== false) {
            return $token;
        } else {
            Err::add('生成token失败');
        }
    }

    /**
     * 验证token
     * @param $token
     * @param string $type
     * @return array|bool|\PDOStatement|string|\think\Model|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function verifyToken($token,$devsn,$type,$keyer) {
        $uid = substr(base64_decode(substr($token, 32), true), 32);
        $redisKey = $keyer . "_{$type}";
        $redisModel = Redis::getInstance();
        // 获取token
        $tokenData = $redisModel->hget($redisKey, $uid);
        $tokenData = json_decode($tokenData, true);
        if (!$tokenData) {
            Err::add('请登录访问', RespCode::TOKEN_EMPTY);
            return false;
        }
        // 需要验证phone
        if (defined('IS_PHONE') && $tokenData['is_openid']) {
            Err::add('必须授权电话', RespCode::MUST_BIND_PHONE);
            return false;
        }
        //是否同一个设备登录
        if(!empty($devsn) && !empty($tokenData['devsn'])){
            if($devsn!=$tokenData['devsn']){
                Err::add('您的账号已在另一台设备登录', RespCode::TOKEN_OFF_SITE);
                return false;
            }
        }
        // token验证
        if ($tokenData['token'] != $token) {
            Err::add('您的账号已在另一台设备登录', RespCode::TOKEN_OFF_SITE);
            return false;
        }
        // 过期验证
        if ($tokenData['expire'] < time()) {
            Err::add('登录过期', RespCode::TOKEN_EXPIRE);
            return false;
        }
        // 获取用户详情
        $info = [
            'id'=>$uid
        ];
        if (!$tokenData['is_openid'] && !$info = $this->getInfo($type, $uid)) {
            Err::add('用户不存在',RespCode::TOKEN_ERR);
            return false;
        }

        return $info;
    }

    /**
     * 获取用户信息
     * @param $type
     * @param $uid
     * @return array|bool|\PDOStatement|string|\think\Model|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    private function getInfo($type, $uid) {
        switch ($type) {
            case self::API_TOKEN:
                $field = '*';
//                return User::getInfo($uid, $field);
                return CompanyAgent::getInfo($uid, $field);
                break;
            case self::APIENC_TOKEN:
                $field = '*';
//                return User::getInfo($uid, $field);
                return CompanyAgentenc::getInfo($uid, $field);
                break;
            case self::WECHAT_TOKEN:
                $field = '*';
                return User::getInfo($uid, $field);
                break;
            case self::ADMIN_TOKEN:
                return Admin::getInfo($uid);
                break;
            case self::BADMIN_TOKEN:
                return CompanyAccount::where('id',$uid)->where('deleted',0)->find();
                  default:
                Err::add('类型错误');
                return false;
                break;
        }
    }


    /**
     * 生成手机号令牌
     * @param $uid
     * @param string $type
     * @param int $expire
     * @return bool|string
     */
    public function getMobileToken($str, $type = self::API_TOKEN, $expire = self::EXPIRE_TIME) {
        $token = strtoupper(md5(get_nonce_str() . $str . time())) . base64_encode(get_nonce_str() . $str);
        $redisKey = RedisKey::MOBILE_VERIFY . "_{$type}";
        $redisModel = Redis::getInstance();
        $tokenData = [
            'expire' => time() + $expire,
            'token'  => $token,
        ];
        if ($redisModel->hset($redisKey, $str, json_encode($tokenData)) !== false) {
            return $token;
        } else {
            Err::add('生成令牌失败');
            throw new Exception('生成令牌失败', RespCode::TOKEN_ERR);
        }
    }

    /**
     * 验证手机号令牌
     * @param $token
     * @param string $type
     * @return array|bool|\PDOStatement|string|\think\Model|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function verifyMobileToken($token, $type = self::API_TOKEN) {
        $str = substr(base64_decode(substr($token, 32), true), 32);
        $redisKey = RedisKey::MOBILE_VERIFY . "_{$type}";
        $redisModel = Redis::getInstance();
        // 获取token
        $tokenData = $redisModel->hget($redisKey, $str);
        $tokenData = json_decode($tokenData, true);
        if (!$tokenData) {
            Err::add('令牌错误', RespCode::TOKEN_ERR);
            throw new Exception('令牌错误', RespCode::TOKEN_ERR);
        }
        if ($tokenData['token'] != $token) {
            Err::add('令牌错误', RespCode::TOKEN_ERR);
            throw new Exception('令牌错误', RespCode::TOKEN_ERR);
        }
        if ($tokenData['expire'] < time()) {
            Err::add('令牌过期', RespCode::TOKEN_EXPIRE);
            throw new Exception('令牌过期', RespCode::TOKEN_ERR);
        }
        return $str;
    }

    /**
     * 删除用户token
     * @param $token
     * @param string $type
     * @return mixed
     */
    public function delUserToken($token, $type ) {
        $str = substr(base64_decode(substr($token, 32), true), 32);
        $redisKey = RedisKey::APP_TOKEN_HASH . "_{$type}";
        $redisModel = Redis::getInstance();
        $r = $redisModel->hDel($redisKey, $str);
        return $r;
    }


}
